#include <QMessageBox>

#include "clientdialog.h"
#include "ui_clientdialog.h"
#include <QVector>
#include "scheduleindividual.h"

ClientDialog::ClientDialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::ClientDialog)
{
    ui->setupUi(this);
    ui->TxtBox->setReadOnly(true);
    
    socket = new QTcpSocket();
    size = 0;
    
    connect(socket, SIGNAL(readyRead()), this, SLOT(receiveMessage()), Qt::QueuedConnection);
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
}

ClientDialog::~ClientDialog()
{
    delete ui;
    delete socket;
}

void ClientDialog::changeEvent(QEvent *e)
{
    QDialog::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void ClientDialog::sendMessage(QString message){
    
    QByteArray block;
    
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    
    quint16 size = 0;
    out << size;
    out << message;
    size = (quint16) (block.size() - sizeof(quint16));
    out.device()->seek(0);
    out << size;
    
    socket->write(block);
}

void ClientDialog::receiveMessage(){
    
    while (socket->bytesAvailable() > 0){
        
        QDataStream in(socket);
        in.setVersion(QDataStream::Qt_4_0);
        
        if (size == 0) {
            if (socket->bytesAvailable() < (int) sizeof(quint16))
                return;
            
            in >> size;
        }
        
        if (socket->bytesAvailable() < size)
            return;
        
        quint16 t;
        MessageType type;
        in >> t;
        type = (MessageType) t;
        
        QString message;
        in >> message;
        size = 0;
        
        if(type == POPULATION){
            //Assume, if we get here, that the other two have already been passed in
            unpackPopulation(message);
        }
        else if (type == TIMESLOTS){
            bool ok;
            timeSlots = message.toInt(&ok, 10);
        } else {
            //These are schedules
            unpackConflicts(message);
        }
        
    }
}

void ClientDialog::on_ConnectBtn_clicked()
{
    QString serverIP = ui->ServerIPNum->text();
    int serverPort = ui->ServerPortNum->text().toInt();
    
    socket->connectToHost(serverIP, serverPort);
    
    ui->ServerIPNum->setEnabled(false);
    ui->ServerPortNum->setEnabled(false);
}

//Handle socket errors
void ClientDialog::displayError(QAbstractSocket::SocketError){
    
    QMessageBox::critical(this, "Connection Error", "Problem with server connection!");
    
    ui->ConnectBtn->setEnabled(true);
    ui->ServerIPNum->setEnabled(true);
    ui->ServerPortNum->setEnabled(true);
}

void ClientDialog::unpackConflicts(QString str){
    
    QStringList outerList = str.split(":", QString::SkipEmptyParts);
    
    sc = new ScheduleConflicts(outerList.size());
    
    sc->conflicts = new int*[outerList.size()];
    
    for (int i = 0; i < outerList.size(); i++){
        QString inner = outerList.at(i);
        QStringList innerList = inner.split(" ", QString::SkipEmptyParts);
        
        sc->conflicts[i] = new int[innerList.size()];
        
        for (int j = 0; j < innerList.size(); j++){
            sc->conflicts[i][j] = innerList.at(j).toInt();
        }
    }
}

void ClientDialog::unpackPopulation(QString str){
    //NEED TO PUT THE COMPLETED INDIVIDUALS SOMEWHERE
    QVector<ScheduleIndividual*>* vec = new QVector<ScheduleIndividual*>();
    
    //This gives a list of individuals, separated by whitespace
    QStringList indivList = str.split(":", QString::SkipEmptyParts);
    
    int indivsLength;
    
    for (int i = 0; i < indivList.size(); i++){
        //This gets one particular individual
        QString oneIndiv = indivList.at(i);
        QStringList indivIntsList = oneIndiv.split(" ", QString::SkipEmptyParts);
        
        //Need to get this in order to use for size when initializing pop
        indivsLength = indivIntsList.size();
        
        ScheduleIndividual* id = new ScheduleIndividual(indivIntsList.size(), sc, timeSlots);
        id->populateFromList(indivIntsList);
        
        vec->push_back(id);
    }
    
    sPop = new SchedulePopulation(indivList.size(), indivsLength, sc, timeSlots);
    sPop->populateFromVector(vec);

    delete vec;
    
    int genNumber = 0;
    double bestFitness = sPop->getBestFitness();
    string bestGenes = sPop->getBestGenes();
    
    QString numString;
    numString.setNum(0);
    
    ui->TxtBox->append("Generation: " + numString);
    ui->TxtBox->append(QString::fromStdString("Best Genes: " + bestGenes));
    
    numString.setNum(bestFitness);
    ui->TxtBox->append("BestFitness: " + numString);
    
    for (int j = 0; j < 10000; j++){
        
        genNumber++;
        SchedulePopulation* nextGen = sPop->reproduce();
        delete sPop;
        sPop = nextGen;
        
        double fitness = sPop->getBestFitness();
        string genes = sPop->getBestGenes();
        
        if (fitness > bestFitness){
            bestFitness = fitness;
            bestGenes = genes;
        }
        
        if (genNumber % 100 == 0){
            numString.setNum(genNumber);
            
            ui->TxtBox->append("Generation: " + numString);
            ui->TxtBox->append(QString::fromStdString("Best Genes: " + bestGenes));
            
            numString.setNum(bestFitness);
            ui->TxtBox->append("BestFitness: " + numString);
            ui->TxtBox->append("\n");
            ui->TxtBox->repaint();
        }
    }

    sendMessage(packPopulation(sPop));
}

QString ClientDialog::packPopulation(SchedulePopulation* sp){

    //put all of it into a qstring of ints that are individuals
    QString popString = "";

    for (int i = 0; i < sp->getPopSize(); i++){
        for (int j = 0; j < sp->getIndivSize(); j++){
            int num = sp->sPop->at(i)->myVec->at(j);
            QString str;
            str.setNum(num);
            popString += str;
            popString += " ";
        }
        popString += ":";
    }

    return popString;
}
